package com.tanhua.server.service;

import cn.hutool.core.collection.CollUtil;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.commons.constant.Constants;
import com.tanhua.commons.exception.TanHuaException;
import com.tanhua.dubbo.api.db.UserInfoApi;
import com.tanhua.dubbo.api.mongo.CommentApi;
import com.tanhua.dubbo.api.mongo.MovementApi;
import com.tanhua.dubbo.api.mongo.VisitorApi;
import com.tanhua.model.db.UserInfo;
import com.tanhua.model.enums.CommentType;
import com.tanhua.model.mongo.Comment;
import com.tanhua.model.mongo.Movement;
import com.tanhua.model.mongo.Visitors;
import com.tanhua.model.vo.*;
import com.tanhua.server.interceptor.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 圈子-业务逻辑处理层
 */
@Service
@Slf4j
public class MovementService {

    @DubboReference
    private MovementApi movementApi;

    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private UserInfoApi userInfoApi;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @DubboReference
    private CommentApi commentApi;

    @DubboReference
    private VisitorApi visitorApi;

    /**
     * 动态-发布
     */
    public void postMovement(MovementVo movementVo, MultipartFile[] imageContent) {
        try {
            //1.获取当前用户id
            Long userId = UserHolder.getUserId();
            //2.获取图片urls
            List<String> medias = new ArrayList<>();
            if (null != imageContent && imageContent.length > 0) {
                for (MultipartFile multipartFile : imageContent) {
                    //调用oss获取图片地址 封装到list集合
                    String originalFilename = multipartFile.getOriginalFilename();
                    String imgUrl = ossTemplate.upload(originalFilename, multipartFile.getInputStream());
                    medias.add(imgUrl);
                }
            }
            //3.构造Movement调用服务
            Movement movement = new Movement();
            BeanUtils.copyProperties(movementVo, movement);//文字 经纬度
            movement.setUserId(userId);//动态发布者的用户id
            movement.setLocationName(movementVo.getLocation());//中文位置名称
            movement.setMedias(medias);//图片地址集合
            //4.构造Movement对象调用服务提供者发布动态
            movementApi.publish(movement);
        } catch (IOException e) {
            log.debug("发布动态失败了" + e.getMessage());
            throw new TanHuaException(ErrorResult.error());
        }
    }

    /**
     * 用户动态
     */
    public PageResult<MovementsVo> findPageByUserId(Long page, Long pagesize, Long userId) {
        Long currendUserId = UserHolder.getUserId();//当前用户id
        if (userId != null) {
            currendUserId = userId;
        }
        //1 调用服务提供者 根据分页参数以及userId 分页查询动态发布表
        PageResult<Movement> movementPageResult = movementApi.findPageByUserId(page, pagesize, currendUserId);
        if (movementPageResult == null || CollectionUtils.isEmpty(movementPageResult.getItems())) {
            return new PageResult<>(0l, 10l, 0l, 1l, null);
        }
        //2 循环遍历动态发布表 根据动态发布的用户id 调用服务提供者 查询用户信息（tb_user_info）
        //2.1 将List<Movement> 转为 List<MovementsVo>
        List<MovementsVo> movementsVoList = new ArrayList<>();
        UserInfo userInfo = userInfoApi.getUserInfo(currendUserId);//用户信息
        for (Movement movement : movementPageResult.getItems()) {
            //Long movementUserId = movement.getUserId();//动态发布者的用户id
            MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
            String key =Constants.MOVEMENTS_INTERACT_KEY+movement.getId().toHexString();//固定的写死 理解为表名 MOVEMENTS_INTERACT_动态id
            String hashKey =Constants.MOVEMENT_LIKE_HASHKEY+UserHolder.getUserId();//某个用户对某个动态点赞key MOVEMENT_LIKE_1 MOVEMENT_LOVE_1
            if(stringRedisTemplate.opsForHash().hasKey(key,hashKey)){
                movementsVo.setHasLiked(1);
            }
            movementsVoList.add(movementsVo);
        }
        //3 构造PageResult<MovementsVo>进行返回
        PageResult<MovementsVo> voPageResult = new PageResult<>();
        BeanUtils.copyProperties(movementPageResult, voPageResult);//分页数据
        voPageResult.setItems(movementsVoList);
        return voPageResult;

    }

    /**
     * 好友动态
     */
    public PageResult<MovementsVo> findFriendMovements(Long page, Long pagesize) {
        Long userId = UserHolder.getUserId();
        //1 调用服务根据用户id分页查询好有时间线数据 再查询动态表
        PageResult<Movement> movementPageResult = movementApi.findFriendMovements(page, pagesize, userId);
        if (movementPageResult == null || CollectionUtils.isEmpty(movementPageResult.getItems())) {
            return new PageResult<>(0l, 10l, 0l, 1l, null);
        }
        List<MovementsVo> movementsVos = convert2Vo(movementPageResult.getItems());
        //4 返回Vo
        return new PageResult<>(page, pagesize, movementPageResult.getCounts(), movementsVos);
    }

    /**
     * 好友动态 推荐动态公共方法实现
     * 将List<Movement> 以及 用户信息 构造  List<MovementsVo>
     *
     * @param movementList
     * @return
     */
    private List<MovementsVo> convert2Vo(List<Movement> movementList) {
        //定义List<MovementsVo> 返回
        List<MovementsVo> movementsVoList = new ArrayList<>();
        //收集所有用户ids
        List<Long> listUserIds = movementList.stream().map(Movement::getUserId).collect(Collectors.toList());
        //List<Long> userIds = CollUtil.getFieldValues(movementList, "userId", Long.class);
        //2 获取动态的所有作者的id，再查询用户信息
        Map<Long, UserInfo> userInfoMap = userInfoApi.findByIds(listUserIds, null);
        //3 基于动态表与用户信息 构造Vo
        for (Movement movement : movementList) {
            MovementsVo movementsVo = MovementsVo.init(userInfoMap.get(movement.getUserId()), movement);
            String key =Constants.MOVEMENTS_INTERACT_KEY+movement.getId().toHexString();//固定的写死 理解为表名 MOVEMENTS_INTERACT_动态id
            String likeHashKey =Constants.MOVEMENT_LIKE_HASHKEY+UserHolder.getUserId();//某个用户对某个动态点赞key MOVEMENT_LIKE_1 MOVEMENT_LOVE_1
            if(stringRedisTemplate.opsForHash().hasKey(key,likeHashKey)){
                movementsVo.setHasLiked(1);
            }
            String loveHashKey =Constants.MOVEMENT_LOVE_HASHKEY+UserHolder.getUserId();//某个用户对某个动态点赞key MOVEMENT_LIKE_1 MOVEMENT_LOVE_1
            if(stringRedisTemplate.opsForHash().hasKey(key,loveHashKey)){
                    movementsVo.setHasLoved(1);
            }
            movementsVoList.add(movementsVo);
        }
        return movementsVoList;
    }

    /**
     * 推荐动态
     */
    public PageResult<MovementsVo> recommend(Long page, Long pagesize) {
        String key = Constants.MOVEMENTS_RECOMMEND + UserHolder.getUserId(); //key：MOVEMENTS_RECOMMEND_1  value:1,3,4
        //1 根据key查询redis中Pids set key=Constants.MOVEMENTS_RECOMMEND + UserHolder.getUserId(); value: 1,3,4
        String pids = stringRedisTemplate.opsForValue().get(key);
        //定义List<Movement>
        List<Movement> movementList = new ArrayList<>();
        Long counts ;//总记录数
        if (StringUtils.isEmpty(pids)) {
            //2 没有值 调用服务随机查询10个动态
            movementList = movementApi.randomMovement();
            counts = (long)movementList.size();
        } else {
            //3 有值 根据Pids调用服务分页查询动态数据
            String[] recommendMovementIdsArr = pids.split(","); //pids有可能大于10条
            //String[] recommendMovementIdsArr = StringUtils.split(recommendMovementPids, ",");
            counts = Long.valueOf(recommendMovementIdsArr.length);//总记录数
            long start = (page-1)*pagesize;
            if(counts > start) { //20 > 27
                //分页获取到需要查询的Pids值
                List<Long> listPids = Arrays.stream(recommendMovementIdsArr).skip(start).limit(pagesize).map(idStr -> Long.valueOf(idStr)).collect(Collectors.toList());
                movementList = movementApi.findByPids(listPids);
            }
        }
        //4 调用convert2Vo 将List<Movement>参数传入得到 List<MovementsVo>
        List<MovementsVo> movementsVos = convert2Vo(movementList);
        //5 构造PageResult返回
        return new PageResult<>(page, pagesize, counts, movementsVos);
    }

    /**
     * 单条动态查询
     */
    public MovementsVo findById(String movementId) {
        //1.根据动态id调用服务查询动态数据
        Movement movement = movementApi.findById(movementId);
        //2.根据userId查询userInfo对象
        Long userId = movement.getUserId();
        UserInfo userInfo = userInfoApi.getUserInfo(userId);
        //3.基于动态数据与userInfo构造Vo
        return MovementsVo.init(userInfo,movement);
    }
    /**
     * 动态点赞
     */
    public Integer like(String movementId) {
        //1.构造评论对象保持点赞记录
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));//动态主键id
        comment.setCommentType(CommentType.LIKE.getType());//评论类型：1-点赞，2-评论，3-喜欢
        comment.setUserId(UserHolder.getUserId());//评论人的用户id
        Integer count = commentApi.add(comment);
        //2.将点赞记录写入redis (当查询动态分页数据时候 可以根据redis有当前用户点赞记录返回0或1)
        //方式一（不推荐）
        //String key = "like_"+movementId+"_"+UserHolder.getUserId();
        //stringRedisTemplate.opsForValue().set(key,"xxxx");
        //方式二（推荐）
        String key =Constants.MOVEMENTS_INTERACT_KEY+movementId;//固定的写死 理解为表名 MOVEMENTS_INTERACT_动态id
        String hashKey =Constants.MOVEMENT_LIKE_HASHKEY+UserHolder.getUserId();//某个用户对某个动态点赞key MOVEMENT_LIKE_1 MOVEMENT_LOVE_1
        stringRedisTemplate.opsForHash().put(key,hashKey,"1");
        return count;
    }

    /**
     * 动态喜欢
     */
    public Integer love(String movementId) {
        //1.构造评论对象保持喜欢记录
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));//动态主键id
        comment.setCommentType(CommentType.LOVE.getType());//评论类型：1-点赞，2-评论，3-喜欢
        comment.setUserId(UserHolder.getUserId());//评论人的用户id
        Integer count = commentApi.add(comment);
        //2.将喜欢记录写入redis (当查询动态分页数据时候 可以根据redis有当前用户喜欢记录返回0或1)
        String key =Constants.MOVEMENTS_INTERACT_KEY+movementId;//固定的写死 理解为表名 MOVEMENTS_INTERACT_动态id
        String hashKey =Constants.MOVEMENT_LOVE_HASHKEY+UserHolder.getUserId();//某个用户对某个动态点赞key MOVEMENT_LIKE_1 MOVEMENT_LOVE_1
        stringRedisTemplate.opsForHash().put(key,hashKey,"1");
        return count;
    }

    /**
     * 动态取消点赞
     */
    public Integer dislike(String movementId) {
        //1.构造评论对象调用服务删除点赞记录
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));//动态主键id
        comment.setCommentType(CommentType.LIKE.getType());//评论类型：1-点赞，2-评论，3-喜欢
        comment.setUserId(UserHolder.getUserId());//评论人的用户id
        Integer count = commentApi.remove(comment);
        //2.根据hashKey删除redis中点赞记录
        String key =Constants.MOVEMENTS_INTERACT_KEY+movementId;//固定的写死 理解为表名 MOVEMENTS_INTERACT_动态id
        String hashKey =Constants.MOVEMENT_LIKE_HASHKEY+UserHolder.getUserId();//某个用户对某个动态点赞key MOVEMENT_LIKE_1 MOVEMENT_LOVE_1
        stringRedisTemplate.opsForHash().delete(key,hashKey);
        return count;
    }

    /**
     * 动态取消喜欢
     */
    public Integer unlove(String movementId) {
        //1.构造评论对象调用服务删除喜欢记录
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));//动态主键id
        comment.setCommentType(CommentType.LOVE.getType());//评论类型：1-点赞，2-评论，3-喜欢
        comment.setUserId(UserHolder.getUserId());//评论人的用户id
        Integer count = commentApi.remove(comment);
        //2.根据hashKey删除redis中喜欢记录
        String key =Constants.MOVEMENTS_INTERACT_KEY+movementId;//固定的写死 理解为表名 MOVEMENTS_INTERACT_动态id
        String hashKey =Constants.MOVEMENT_LOVE_HASHKEY+UserHolder.getUserId();//某个用户对某个动态点赞key MOVEMENT_LIKE_1 MOVEMENT_LOVE_1
        stringRedisTemplate.opsForHash().delete(key,hashKey);
        return count;
    }

    /**
     * 谁看过我
     */
    public List<VisitorsVo> findLast4Visitors() {
        //1根据当前用户id查询上次访问消息（最近访客消息）时间
        String key = Constants.VISITORS_USER;
        String hashKey = UserHolder.getUserId().toString();
        //stringRedisTemplate.opsForHash().put(key,hashKey,"上次访问消息时间");
        String lastVisitorTime = (String)stringRedisTemplate.opsForHash().get(key, hashKey);//上次访问消息时间
        //2调用服务查询4条访客记录（最近访客消息查询时间  当前用户id）
        List<Visitors> visitorsList = visitorApi.findLast4Visitors(Long.valueOf(lastVisitorTime),UserHolder.getUserId());
        //3取出所有的访客用户ids
        List<VisitorsVo> visitorsVoList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(visitorsList)){
            List<Long> visitorUserIds = visitorsList.stream().map(Visitors::getVisitorUserId).collect(Collectors.toList());
            Map<Long, UserInfo> userInfoMap = userInfoApi.findByIds(visitorUserIds, null);
            //4基于用户详情和访客对象构造Vo返回
            for (Visitors visitors : visitorsList) {
                visitorsVoList.add(VisitorsVo.init(userInfoMap.get(visitors.getVisitorUserId()),visitors));
            }
        }
        return visitorsVoList;
    }

}
